package Range;

import lombok.Data;

import java.util.function.Function;
import java.util.function.Predicate;

@Data
public final class Range<T> {
    private T from;
    private T to;
    private boolean includeLower = true;
    private boolean includeUpper = false;

    // 保证至少有一个边界，不允许直接构造
    private Range() {
    }

    public Range(T from, T to) {
        this.from = from;
        this.to = to;
    }

    /**
     * 从字符串解析，字符串格式：(a,b]，小括号不包括对应值，中括号包括对应的值，左值缺失表示小于，右值缺失表示大于，不允许左右两个值都空缺
     *
     * @param text
     * @return
     */
    public static <R> Range<R> parse(String text, Function<String, R> converter) {
        String[] strings = text.split(",", -1);
        if (strings.length != 2) {
            throw new RuntimeException("范围解析出错：" + text);
        }
        String left = strings[0].trim();
        String right = strings[1].trim();
        int rightLast = right.length() - 1;
        if (left.isEmpty()
                || right.isEmpty()
                || left.charAt(0) != '(' && left.charAt(0) != '['
                || right.charAt(rightLast) != ')' && right.charAt(rightLast) != ']') {
            throw new RuntimeException("范围解析出错：" + text);
        }

        String fromStr = left.substring(1).trim();
        String toStr = right.substring(0, rightLast).trim();
        if (fromStr.isEmpty() && toStr.isEmpty()) {
            throw new RuntimeException("至少需要有一个边界：" + text);
        }
        Range<R> range = new Range<R>();
        if (!fromStr.isEmpty()) {
            range.from = converter.apply(fromStr);
        }
        if (!toStr.isEmpty()) {
            range.to = converter.apply(toStr);
        }
        if (range.from instanceof Comparable && range.to instanceof Comparable && ((Comparable) range.from).compareTo(range.to) >= 0) { // 左边界大于等于右边界
            throw new RuntimeException("左边界必须小于右边界：" + text);
        }
        range.includeLower = left.charAt(0) == '[';
        range.includeUpper = right.charAt(rightLast) == ']';

        return range;
    }

    public <R> Range<R> convert(Function<T, R> converter) {
        Range<R> range = new Range<R>();
        range.from = converter.apply(from);
        range.to = converter.apply(to);
        if (range.from instanceof Comparable && range.to instanceof Comparable && ((Comparable) range.from).compareTo(range.to) >= 0) { // 左边界大于等于右边界
            throw new RuntimeException("转换后的左边界必须小于右边界：" + range);
        }
        range.includeLower = includeLower;
        range.includeUpper = includeUpper;
        return range;
    }

    public static <T extends Comparable> Predicate<Range<T>> predicate(T t) {
        return range -> {
            boolean r = true;
            if (range.to != null) {
                int i = t.compareTo(range.to);
                r &= i < 0 || range.includeUpper && i == 0;
            }
            if (range.from != null) {
                int i = t.compareTo(range.from);
                r &= i > 0 || range.includeLower && i == 0;
            }
            return r;
        };
    }

    public <T extends Comparable> boolean contain(T t) {
        boolean r = true;
        if (this.to != null) {
            int i = t.compareTo(this.to);
            r &= i < 0 || this.includeUpper && i == 0;
        }
        if (this.from != null) {
            int i = t.compareTo(this.from);
            r &= i > 0 || this.includeLower && i == 0;
        }
        return r;
    }

}
